/*
 * BlockingQueue.h
 *
 *  Created on: Sep 10, 2011
 *      Author: Gherman Alin
 */

#ifndef BLOCKINGQUEUE_H_
#define BLOCKINGQUEUE_H_

#include <vector>
#include <boost/utility.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/thread.hpp>

namespace Threads {

template<typename T>
class BlockingQueue {
public:
	BlockingQueue(int size) :
			begin(0), end(0), buffered(0), circular_buf(size) {
	}
	virtual ~BlockingQueue() {
	}

	void put(const T& m) {
		boost::mutex::scoped_lock lk(monitor);
		while (buffered == circular_buf.size()) {
			buffer_not_full.wait(lk);
		}
		circular_buf[end] = m;
		end = (end + 1) % circular_buf.size();
		++buffered;
		buffer_not_empty.notify_one();
	}

	T take() {
		boost::mutex::scoped_lock lk(monitor);
		while (buffered == 0) {
			buffer_not_empty.wait(lk);
		}
		T i = circular_buf[begin];
		begin = (begin + 1) % circular_buf.size();
		--buffered;
		buffer_not_full.notify_one();
		return i;
	}

	bool tryPut(const T& m, unsigned long timeout) {
		boost::mutex::scoped_lock lk(monitor);
		while (buffered == circular_buf.size()) {
			if (!buffer_not_full.timed_wait(lk, boost::posix_time::milliseconds(timeout))) {
				return false;
			}
		}
		circular_buf[end] = m;
		end = (end + 1) % circular_buf.size();
		++buffered;
		buffer_not_empty.notify_one();
		return true;
	}

	bool tryTake(unsigned long timeout, T& out) {
		boost::mutex::scoped_lock lk(monitor);
		while (buffered == 0) {
			if (!buffer_not_empty.timed_wait(lk, boost::posix_time::milliseconds(timeout))) {
				return false;
			}
		}
		out = circular_buf[begin];
		begin = (begin + 1) % circular_buf.size();
		--buffered;
		buffer_not_full.notify_one();
		return true;
	}

private:
	unsigned int begin, end, buffered;
	std::vector<T> circular_buf;
	boost::condition buffer_not_full, buffer_not_empty;
	boost::mutex monitor;

};

}
#endif
